package cn.sh.library.mgts.service.impl;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.ClearScrollResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery.ScoreMode;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder.FilterFunctionBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
import org.elasticsearch.index.query.functionscore.WeightBuilder;
import org.elasticsearch.index.search.QueryStringQueryParser;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms.Bucket;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import cn.sh.library.mgts.base.ResponseCodeEnums;
import cn.sh.library.mgts.bean.SearchMap;
import cn.sh.library.mgts.common.Constant;
import cn.sh.library.mgts.dto.Pager;
import cn.sh.library.mgts.dto.SearchExpression;
import cn.sh.library.mgts.framework.exception.CustomRuntimeException;
import cn.sh.library.mgts.service.HorizonService;

@Service
public class HorizonServiceImpl implements HorizonService {
	@Autowired
	RestHighLevelClient highLevelClient;

	final String bulkPath = "C:\\TempFiles\\generatefiles\\es\\horizon\\";

	private static Logger logger = LoggerFactory.getLogger(HorizonServiceImpl.class);
	
	
	@Override
	public Map<String, Object> freetextFacet(SearchMap searchMap){
		Map<String, Object> resMap = new HashMap<String, Object>();

		//判断条件是否为空
		if(StringUtils.isBlank(searchMap.getFreetext())) {
			throw new CustomRuntimeException(ResponseCodeEnums.EMPTY_INSPECTION_ERROR);
		}

		//判断表达式检索
		boolean isExpr = isExpr(searchMap.getFreetext());
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		if(isExpr) {
			SearchExpression searchExpression = new SearchExpression(searchMap.getFreetext());
			BoolQueryBuilder queryBuilder = new BoolQueryBuilder();
			try {
				queryBuilder = searchExpression.getQueryBuilder();
			} catch (Exception e) {
				throw new CustomRuntimeException(ResponseCodeEnums.NO_EXPRE_FIELD_ERROR, e.getMessage());
			}
			
			List<String> keywords = searchExpression.getValues();
			searchSourceBuilder.query(queryBuilder);
		}else {
			//检查禁检词
			List<String> keywords = Arrays.asList(searchMap.getFreetext().trim().split(" "));
			//条件
			BoolQueryBuilder queryBuilder = buildFreetextQuerys(keywords);
			searchSourceBuilder.query(queryBuilder);
		}
		
		//分面-loc
		TermsAggregationBuilder locAggregation = AggregationBuilders.terms("agg_loc")
				.field("location.keyword").size(999);

		//分面-col
		TermsAggregationBuilder colAggregation = AggregationBuilders.terms("agg_col")
				.field("collection.keyword").size(999);
		
		//分面-publis_date
		TermsAggregationBuilder pubDateAggregation = AggregationBuilders.terms("agg_pub_date")
				.field("view.publish_date.keyword").size(20);
		
		searchSourceBuilder.size(0);
		
		searchSourceBuilder.aggregation(colAggregation).aggregation(locAggregation).aggregation(pubDateAggregation);
		
		SearchRequest searchRequest = new SearchRequest(Constant.BIB_INDEX_NAME);
		searchRequest.source(searchSourceBuilder);
		
		try {
			SearchResponse searchResponse = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);
			logger.info("用时:{}", searchResponse.getTook()); 
			//- loc
			Terms locTerms = searchResponse.getAggregations().get("agg_loc");
			List<Map<String, Object>> locAggs = new ArrayList<Map<String, Object>>();
			resMap.put("facet_loc", locAggs);
			for(Bucket bucket : locTerms.getBuckets()) {
				Map<String, Object> facet = new HashMap<String, Object>();
				facet.put("name", bucket.getKey());
				facet.put("count", bucket.getDocCount());
				locAggs.add(facet);
			}
			//- col
			Terms colTerms = searchResponse.getAggregations().get("agg_col");
			List<Map<String, Object>> colAggs = new ArrayList<Map<String, Object>>();
			resMap.put("facet_col", colAggs);
			for(Bucket bucket : colTerms.getBuckets()) {
				Map<String, Object> facet = new HashMap<String, Object>();
				facet.put("name", bucket.getKey());
				facet.put("count", bucket.getDocCount());
				colAggs.add(facet);
			}
			//- pub_date
			Terms pubDateTerms = searchResponse.getAggregations().get("agg_pub_date");
			List<Map<String, Object>> pubDateAggs = new ArrayList<Map<String, Object>>();
			resMap.put("facet_pub_date", pubDateAggs);
			for(Bucket bucket : pubDateTerms.getBuckets()) {
				Map<String, Object> facet = new HashMap<String, Object>();
				facet.put("name", bucket.getKey());
				facet.put("count", bucket.getDocCount());
				pubDateAggs.add(facet);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return resMap;
	}
	
	
	private boolean isExpr(String freetext) {
		return freetext.matches("(.*)[A-Z]=(.*)") ;
	}
	
	@Override
	public List<Map<String, String>> freetextSearch(SearchMap searchMap, Pager pager){
		List<Map<String, String>> res = new ArrayList<>();
		
		//判断条件是否为空
		if(StringUtils.isBlank(searchMap.getFreetext())) {
			throw new CustomRuntimeException(ResponseCodeEnums.EMPTY_INSPECTION_ERROR);
		}
		
		//判断表达式检索
		boolean isExpr = isExpr(searchMap.getFreetext());

		//构造检索条件
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		//检查禁检词
		List<String> keywords;
		if(isExpr) {
			SearchExpression searchExpression = new SearchExpression(searchMap.getFreetext());
			BoolQueryBuilder queryBuilder = new BoolQueryBuilder();
			try {
				queryBuilder = searchExpression.getQueryBuilder();
			} catch (Exception e) {
				throw new CustomRuntimeException(ResponseCodeEnums.NO_EXPRE_FIELD_ERROR, e.getMessage());
			}
			BoolQueryBuilder limitQueryBuilder = buildLimitQuery(searchMap);
			if(limitQueryBuilder.hasClauses()) {
				queryBuilder.must(limitQueryBuilder);
			}
			keywords = searchExpression.getValues();
			searchSourceBuilder.query(queryBuilder);
			
		}else {
			keywords = Arrays.asList(searchMap.getFreetext().trim().split(" "));
			
			//score权重- ISBN(100)、题名(50)、著者(10)、出版社(5)
			FilterFunctionBuilder[] list = new FilterFunctionBuilder[4]; 

			//ISBN
			ScoreFunctionBuilder<WeightBuilder> iSBNScore = new WeightBuilder();
			iSBNScore.setWeight(100);
			QueryBuilder iSBNQueryBuilder = builderFunctQuery("search.isbn_term", keywords);
			FilterFunctionBuilder iSBNFuncBuilder = new FilterFunctionBuilder(iSBNQueryBuilder, iSBNScore);
			list[0] = iSBNFuncBuilder;
			
			//title
			ScoreFunctionBuilder<WeightBuilder> titleScore = new WeightBuilder();
			titleScore.setWeight(50);
			QueryBuilder titleQueryBuilder = builderFunctQuery("search.title_keyword", keywords);
			FilterFunctionBuilder titleFuncBuilder = new FilterFunctionBuilder(titleQueryBuilder, titleScore);
			list[1] = titleFuncBuilder;

			//author
			ScoreFunctionBuilder<WeightBuilder> creatorScore = new WeightBuilder();
			creatorScore.setWeight(40);
			QueryBuilder creatorQueryBuilder = builderFunctQuery("search.author_keyword", keywords);
			FilterFunctionBuilder creatorFuncBuilder = new FilterFunctionBuilder(creatorQueryBuilder, creatorScore);
			list[2] = creatorFuncBuilder;

			//publisher
			ScoreFunctionBuilder<WeightBuilder> publisherScore = new WeightBuilder();
			publisherScore.setWeight(5);
			QueryBuilder publisherQueryBuilder = builderFunctQuery("search.publisher_keyword", keywords);
			FilterFunctionBuilder publisherFuncBuilder = new FilterFunctionBuilder(publisherQueryBuilder, publisherScore);
			list[3] = publisherFuncBuilder;

			BoolQueryBuilder queryBuilder = buildFreetextQuerys(keywords);
			BoolQueryBuilder limitQueryBuilder = buildLimitQuery(searchMap);
			if(limitQueryBuilder.hasClauses()) {
				queryBuilder.must(limitQueryBuilder);
			}
			
			FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(queryBuilder, list).scoreMode(ScoreMode.MAX).boostMode(CombineFunction.MAX);
			searchSourceBuilder.query(functionScoreQueryBuilder);
		} 
		
		if(pager == null) {
			pager = new Pager();
		}
		searchSourceBuilder.from(pager.getStartIndex());
		searchSourceBuilder.size(pager.getPageSize());
		searchSourceBuilder.sort("_score").sort(buildOrderCdtn(searchMap));
		SearchRequest searchRequest = new SearchRequest(Constant.BIB_INDEX_NAME);
		searchRequest.source(searchSourceBuilder);
		
		try {
			SearchResponse searchResponse = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);
			logger.info("用时:{}", searchResponse.getTook()); 
			long total = searchResponse.getHits().getTotalHits().value;// 返回数量的总数
			pager.calcPageCount(total);
			pager.setTook(searchResponse.getTook().getMillis());
			searchResponse.getHits().forEach(action -> {
				Map<String, String> viewMap = new HashMap<>();
				viewMap = (Map<String, String>) action.getSourceAsMap().get("view");
				viewMap.put("bibno", action.getSourceAsMap().get("bibno").toString());
				res.add(viewMap);
			});
		} catch (Exception e) {
			e.printStackTrace();
		}
		return res;
	}
	
	
	private FieldSortBuilder buildOrderCdtn(SearchMap searchMap) {
		if (searchMap.getSort() != null && searchMap.getSort().containsKey("field") && searchMap.getSort().containsKey("type")) {
			if (searchMap.getSort().get("field").equals("title")) {
				if ((!StringUtils.isEmpty(searchMap.getSort().get("type")))
						&& searchMap.getSort().get("type").equals("desc")) {
					return new FieldSortBuilder("sort.title.keyword").order(SortOrder.DESC);
				} else {
					return new FieldSortBuilder("sort.title.keyword").order(SortOrder.ASC);
				}
			} else if (searchMap.getSort().get("field").equals("date")) {
				if ((!StringUtils.isEmpty(searchMap.getSort().get("type")))
						&& searchMap.getSort().get("type").equals("desc")) {
					return new FieldSortBuilder("sort.date").order(SortOrder.DESC);
				} else {
					return new FieldSortBuilder("sort.date").order(SortOrder.ASC);
				}
			}
		}
		
		return new FieldSortBuilder("sort.title.keyword").order(SortOrder.ASC);
	}
	
	//权重条件
	private QueryBuilder builderFunctQuery(String field, List<String> keywords) {
		BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
		for(String keyword : keywords) {
			boolQueryBuilder.should(QueryBuilders.matchPhraseQuery(field, QueryStringQueryParser.escape(keyword)));
		}
		return boolQueryBuilder;
	}
	
	//关键字条件构造
	private BoolQueryBuilder buildFreetextQuerys(List<String> keywords) {
		BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
		
		return boolQueryBuilder;
	}
	
	//限定条件构造
	private BoolQueryBuilder buildLimitQuery(SearchMap searchMap) {
		BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
		
		if (searchMap.getLimit() != null && searchMap.getLimit().size() != 0) {// 有limit的情况下
			if (searchMap.getLimit().containsKey("coll")) {
				BoolQueryBuilder colQ = QueryBuilders.boolQuery();
				String[] collectionList = searchMap.getLimit().get("coll").trim().split("\\s+");
				for (int i = 0; i < collectionList.length; i++) {
					String coll = collectionList[i].toLowerCase();
					if(StringUtils.isNotBlank(coll)) {
						colQ.should(QueryBuilders.termQuery("collection", coll));
					}
				}
				if(colQ.should().size() > 0) {
					queryBuilder.must(colQ);
				}
			}
			if (searchMap.getLimit().containsKey("loc")) {
				BoolQueryBuilder locQ = QueryBuilders.boolQuery();
				String[] locationList = searchMap.getLimit().get("loc").trim().split("\\s+");// 字母简写
				for (int i = 0; i < locationList.length; i++) {
					String loc = locationList[i].toLowerCase();
					if(StringUtils.isNotBlank(loc)) {
						locQ.should(QueryBuilders.termQuery("location", loc));
					}
				}
				if(locQ.should().size() > 0) {
					queryBuilder.must(locQ);
				}
			}
			if (searchMap.getLimit().containsKey("date")) {
				BoolQueryBuilder dateQ = QueryBuilders.boolQuery();
				String[] datelist = searchMap.getLimit().get("date").trim().split("\\s+");// 字母简写
				for (int i = 0; i < datelist.length; i++) {
					String date = datelist[i];
					if(StringUtils.isNotBlank(date)) {
						dateQ.should(QueryBuilders.termQuery("view.publish_date.keyword", date));
					}
				}
				if(dateQ.should().size() > 0) {
					queryBuilder.must(dateQ);
				}
			}
		}
		return queryBuilder;
	}
	
	@Override
	public List<Map<String, String>> searchQuery(SearchMap searchMap, Pager pager) {
		List<Map<String, String>> res = new ArrayList<>();
		SearchSourceBuilder builder = new SearchSourceBuilder();

		BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();

		BoolQueryBuilder limitQueryBuilder = buildLimitQuery(searchMap);
		if(limitQueryBuilder.hasClauses()) {
			queryBuilder.must(limitQueryBuilder);
		}

		// 排序
		if (searchMap.getSort() != null && searchMap.getSort().containsKey("field") && searchMap.getSort().containsKey("type")) {
			if (searchMap.getSort().get("field").equals("title")) {
				if ((!StringUtils.isEmpty(searchMap.getSort().get("type")))
						&& searchMap.getSort().get("type").equals("desc")) {
					builder.sort(new FieldSortBuilder("sort.title.keyword").order(SortOrder.DESC));
				} else {
					builder.sort(new FieldSortBuilder("sort.title.keyword").order(SortOrder.ASC));
				}
			} else if (searchMap.getSort().get("field").equals("date")) {
				if ((!StringUtils.isEmpty(searchMap.getSort().get("type")))
						&& searchMap.getSort().get("type").equals("desc")) {
					builder.sort(new FieldSortBuilder("sort.date").order(SortOrder.DESC));
				} else {
					builder.sort(new FieldSortBuilder("sort.date").order(SortOrder.ASC));
				}
			}
		} else {
			builder.sort(new FieldSortBuilder("sort.title.keyword").order(SortOrder.ASC));
		}

		builder.size(pager.getPageSize());
		builder.from(pager.getStartIndex());

		builder.query(queryBuilder);

		SearchRequest searchRequest = new SearchRequest(Constant.BIB_INDEX_NAME);
		searchRequest.source(builder);
		try {
			SearchResponse searchResponse = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);
			logger.info("用时:{}", searchResponse.getTook()); 
			long total = searchResponse.getHits().getTotalHits().value;// 返回数量的总数
			pager.calcPageCount(total);
			pager.setTook(searchResponse.getTook().getMillis());
			searchResponse.getHits().forEach(action -> {
				Map<String, String> viewMap = new HashMap<>();
				viewMap = (Map<String, String>) action.getSourceAsMap().get("view");
				viewMap.put("bibno", action.getSourceAsMap().get("bibno").toString());
				res.add(viewMap);
			});
		} catch (Exception e) {
			e.printStackTrace();
		}
		return res;
	}

	//根据bibno查询bib的数据的view
	@Override
	public Map<String, Object> bibQuery(String bib) {
		//传入bib号 根据bib号匹配es库中对应数据
		SearchSourceBuilder builder = new SearchSourceBuilder();
		BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
		queryBuilder.must(QueryBuilders.matchPhraseQuery("_id", bib));
		builder.query(queryBuilder);
		
		Map<String, Object> res = new HashMap<>();

		SearchRequest searchRequest = new SearchRequest(Constant.BIB_INDEX_NAME);
		searchRequest.source(builder);
		try {
			SearchResponse searchResponse = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);
			long total = searchResponse.getHits().getTotalHits().value;// 返回数量的总数
			if(total > 0) {
				res = (Map<String, Object>) searchResponse.getHits().getAt(0).getSourceAsMap().get("view");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return res;
	}
	
	// search的模糊匹配
	public void searchMatchPhrase(SearchMap searchMap, BoolQueryBuilder queryBuilder, String key, List<String> keywords) {
		if (searchMap.getSearch().get(key) != null) {// 模糊匹配
			String[] search = searchMap.getSearch().get(key).trim().split("\\s+");
			for (int i = 0; i < search.length; i++) {
				keywords.add(search[i]);
//				if(key.endsWith("isbn_term")||key.equals("issn_term")||key.equals("isrc_term")) {
//					queryBuilder.must(QueryBuilders.matchPhraseQuery(("search." + key), search[i].replaceAll("-","")));
//				}else {
//					queryBuilder.must(QueryBuilders.matchPhraseQuery(("search." + key), search[i]));
//				}

				queryBuilder.must(QueryBuilders.matchPhraseQuery(("search." + key), search[i]));
			}
		}
	}

	// search的前方一致//search的前方一致
	public void searchPrefix(SearchMap searchMap, BoolQueryBuilder queryBuilder, String key, List<String> keywords) {
		if (searchMap.getSearch().get(key) != null) {// 模糊匹配
			String[] search = searchMap.getSearch().get(key).trim().split("\\s+");
			for (int i = 0; i < search.length; i++) {
				keywords.add(search[i]);
				queryBuilder.must(QueryBuilders.prefixQuery("search." + key + ".keyword", search[i]));
			}
		}
	}

	@Override
	public boolean scrolldata() {
		final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L));
		SearchRequest searchRequest = new SearchRequest(Constant.BIB_INDEX_NAME);
		searchRequest.scroll(scroll);
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		searchSourceBuilder.query(QueryBuilders.matchAllQuery());
		searchSourceBuilder.size(5000);
		searchRequest.source(searchSourceBuilder);

		SearchResponse searchResponse;
		try {
			int i = 1;
			searchResponse = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);
			String scrollId = searchResponse.getScrollId();
			SearchHit[] searchHits = searchResponse.getHits().getHits();
		    writeTobulkJson(searchResponse.getHits(), String.valueOf(i));

		    logger.info("scrollid:[{}],数量：[{}], 批次：[{}]", scrollId, searchHits.length, i);
		    
			while (searchHits != null && searchHits.length > 0) { 
				try {
					i++;
				    SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId); 
				    scrollRequest.scroll(scroll);
				    searchResponse = highLevelClient.scroll(scrollRequest, RequestOptions.DEFAULT);
				    scrollId = searchResponse.getScrollId();
				    searchHits = searchResponse.getHits().getHits();
				    writeTobulkJson(searchResponse.getHits(), String.valueOf(i));
				    logger.info("scrollid:[{}],数量：[{}], 批次：[{}]", scrollId, searchHits.length, i);
				} catch (Exception e) {
					// TODO: handle exception
					logger.error("",e.getStackTrace());
				}
			}

			ClearScrollRequest clearScrollRequest = new ClearScrollRequest(); 
			clearScrollRequest.addScrollId(scrollId);
			ClearScrollResponse clearScrollResponse = highLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
			boolean succeeded = clearScrollResponse.isSucceeded();
			return succeeded;
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} 

		return false;
	}
	
	private void writeTobulkJson(SearchHits searchHits, String jsonName) {
		String filename = bulkPath;
		File file = new File(filename);
		Gson gson = new GsonBuilder().enableComplexMapKeySerialization().create();
		if (!file.getParentFile().exists()) {// 判断目标文件所在的目录是否存在
			// 如果目标文件所在的文件夹不存在，则创建父文件夹
			if (!file.getParentFile().mkdirs()) {// 判断创建目录是否成功
			}
		}
		try {
			FileWriter writer = new FileWriter(filename + "bulkbib" +jsonName + ".json");
			logger.info(filename + "bulkbib" +jsonName + ".json");
			searchHits.forEach(hit -> {
				try {
					writer.write(gson.toJson(hit.getSourceAsMap()) + "\r\n");
				} catch (IOException e) {
					e.printStackTrace();
				}
			});
			writer.flush();
			writer.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public List<String> bulkFileList(){
		List<String> result = new ArrayList<String>();
		
		File file = new File(bulkPath);
		
		if(file.exists() && file.isDirectory()) {
			for(File json : file.listFiles()) {
				try {
					FileReader fileReader = new FileReader(json) ;
					
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				}
			}
		}
		
		return result;
	}

}
